/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.clazz;
import java.applet.Applet;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.beans.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.List;
import java.util.ArrayList;
import java.util.ResourceBundle;
import java.text.MessageFormat;
import javax.swing.JApplet;
import javax.swing.JButton;
import org.openide.*;
import org.openide.util.*;
import org.openide.cookies.*;
import org.openide.filesystems.*;
import org.openide.loaders.*;
import org.openide.explorer.propertysheet.PropertySheet;
import org.openide.nodes.Node;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.CookieSet;
import org.openide.src.SourceElement;
import org.openide.src.nodes.SourceChildren;
import org.openide.src.nodes.SourceElementFilter;
import org.openide.src.nodes.FilterFactory;
import org.openide.src.nodes.ElementNodeFactory;
/* TODO:
- check the showDeclaredOnly flag - it works different for
variables/constructors than for methods (i.e. for variables/constructors
the declaredOnly are not subset of notDecalredOnly
*/
/**
* DataObject which represents .class files.
*
* @author Jan Jancura, Ian Formanek, Petr Hamernik, Dafe Simonek
*/
public class ClassDataObject extends MultiDataObject implements ElementCookie {
/** generated Serialized Version UID */
static final long serialVersionUID = -7355104884002106137L;
/** constant for applet */
private static final int UNDECIDED = 0;
/** constant for applet */
private static final int APPLET = 1;
/** constant for applet */
private static final int APPLICATION = 2;
/** Name of arguments property. */
private final static String PROP_ARGV = "Arguments"; // NOI18N
/** Name of execution property. */
private final static String PROP_EXECUTION = "Execution"; // NOI18N
// variables ...................................................................................
/** Support for working with class */
transient protected InstanceSupport instanceSupport;
/** Support for executing the class */
transient protected ExecSupport execSupport;
/** The flag for recognizing if cookies are initialized or not */
transient private boolean cookiesInitialized = false;
// constructors ...................................................................................
/** Constructs a new ClassDataObject */
public ClassDataObject (final FileObject fo, final ClassDataLoader loader)
throws org.openide.loaders.DataObjectExistsException {
super (fo, loader);
MultiDataObject.Entry pe = getPrimaryEntry();
instanceSupport = new InstanceSupport.Origin(pe);
execSupport = new ExecSupport(pe);
}
/** Performs cookie initialization. */
private void initCookies () {
// asociate cookies
//boolean isExecutable = false;
Class ourClass = null;
try {
ourClass = instanceSupport.instanceClass();
} catch (IOException ex) {
return;
} catch (ClassNotFoundException ex) {
return;
}
CookieSet cs = getCookieSet();
cs.add(instanceSupport);
if (!(this instanceof SerDataObject)) {
cs.add(execSupport);
}
cs.add(new SourceSupport(ourClass, this));
}
/** Overrides superclass getCookie.<P>
* Lazily initialize cookies. (When they are requested for the first time)
*/
public Node.Cookie getCookie (Class type) {
if (!cookiesInitialized) {
cookiesInitialized = true;
initCookies();
}
return super.getCookie(type);
}
private void readObject (java.io.ObjectInputStream is)
throws java.io.IOException, ClassNotFoundException {
is.defaultReadObject();
cookiesInitialized = false;
}
// DataObject implementation .............................................
/** Help context for this object.
* @return help context
*/
public HelpCtx getHelpCtx () {
return new HelpCtx (ClassDataObject.class);
}
/** Getter for copy action.
* @return true if the object can be copied
*/
public boolean isCopyAllowed () {
boolean isSerializable = false;
try {
isSerializable = Serializable.class.isAssignableFrom(instanceSupport.instanceClass());
} catch (Exception exc) {
// don't allow copying if some error appeared
// during serializability test
}
return isJavaBean () && isSerializable;
}
/** Class DO cannot be moved.
* @return false
*/
public boolean isMoveAllowed () {
return false;
}
/** Class DO cannot be renamed.
* @return false
*/
public boolean isRenameAllowed () {
return false;
}
/** Copies this object to a folder. The copy of the object is required to
* be deletable and movable.
*
* @param f the folder to copy object to
* @exception IOException if something went wrong
* @return the new object
*/
protected DataObject handleCopy (DataFolder f) throws IOException {
String newName = existInFolder (f);
Object bean;
try {
bean = instanceSupport.instanceCreate();
} catch (ClassNotFoundException ex) {
throw new IOException (ex.toString ());
}
if (bean == null) throw new IOException ();
FileObject serFile = f.getPrimaryFile ().createData (newName, "ser"); // NOI18N
FileLock lock = null;
ObjectOutputStream oos = null;
try {
lock = serFile.lock ();
oos = new ObjectOutputStream (serFile.getOutputStream (lock));
oos.writeObject (bean);
}
finally {
if (lock != null)
lock.releaseLock ();
if (oos != null)
oos.close ();
}
return DataObject.find (serFile);
}
/**
* @return class data node
*/
protected Node createNodeDelegate () {
return new ClassDataNode (this);
}
// implementation of ElementCookie ..........................................
/**
* Get the alternate node representation.
* @return the node
* @see org.openide.loaders.DataObject#getNodeDelegate
*/
public Node getElementsParent () {
/* Changed for multiple factories
ClassElementNodeFactory cef = new ClassElementNodeFactory ();
cef.setGenerateForTree (true);
*/
ElementNodeFactory cef = getBrowserFactory();
SourceChildren sourceChildren = new SourceChildren (cef);
SourceElementFilter sourceElementFilter = new SourceElementFilter();
sourceElementFilter.setAllClasses (true);
sourceChildren.setFilter (sourceElementFilter);
try {
Class ourClass = instanceSupport.instanceClass();
sourceChildren.setElement (
new SourceElement(new SourceElementImpl(ourClass, this))
);
} catch (IOException ex) {
} catch (ClassNotFoundException ex) {
}
AbstractNode alteranteParent = new AbstractNode (sourceChildren);
CookieSet cs = alteranteParent.getCookieSet();
cs.add (sourceChildren);
return alteranteParent;
}
// implementation of ExecCookie ..........................................
/**
* @return ExecInfo object for execution.
*/
/*public ExecInfo getExecInfo () {
try {
if (isApplet()) return new AppletExecInfo(getPrimaryFile());
} catch (NbClass.NbClassException e) {
}
// build exec info
String[] args = JavaDataObject.parseParameters(getArgv());
String className = getPrimaryFile().getPackageName('.');
return new ExecInfo (className, args) {
public boolean needsInternalExecution () {
return getExecution ();
}
};
}*/
// implementation of ArgumentsCookie ...................................
/** @return the String arguments to be passed to executed application
* @deprecated
*/
/*public String[] getArguments () {
String[] args;
StringTokenizer st = new StringTokenizer (getArgv ());
args = new String[st.countTokens ()];
int i = 0;
while (st.hasMoreTokens ()) {
args[i++] = st.nextToken ();
}
return args;
}*/
/** @param args the String arguments to be passed to executed application */
/*public void setArguments (String[] args) {
StringBuffer sb = new StringBuffer ();
for (int i = 0; i < args.length; i++) {
sb.append (args[i]);
if (i != args.length - 1) sb.append (" ");
}
try {
setArgv (sb.toString ());
} catch (PropertyVetoException e) {
// arguments vetoed -> cannot do anything, ignore it
}
firePropertyChange ("arguments", null, null);
}*/
/** @return true if the object can get parameters, false otherwise */
/*public boolean getArgumentsSupported () {
return true;
}*/
// Properties implementation .....................................................................
public boolean isInterface () {
return instanceSupport.isInterface ();
}
public Class getSuperclass () throws IOException, ClassNotFoundException {
return instanceSupport.instanceClass ().getSuperclass ();
}
public String getModifiers () throws IOException, ClassNotFoundException {
return Modifier.toString (instanceSupport.instanceClass().getModifiers());
}
public String getClassName () {
try {
return instanceSupport.instanceClass ().getName ();
} catch (Exception e) {
// ignore and return the instance name from the InstanceSupport,
// which is by default the file name of the original instance
}
return instanceSupport.instanceName ();
}
public Class getBeanClass () throws IOException, ClassNotFoundException {
return instanceSupport.instanceClass ();
}
public boolean isExecutable () {
return instanceSupport.isExecutable ();
}
public boolean isJavaBean () {
return instanceSupport.isJavaBean();
}
public boolean isApplet () {
return instanceSupport.isApplet ();
}
/** Sets parameters of the class
*/
void setParams (final String params) throws IOException {
execSupport.setArguments(Utilities.parseParameters(params));
}
/** Returns parameters of the class
*/
String getParams () {
String[] parArray = execSupport.getArguments();
StringBuffer buf = new StringBuffer();
for (int i = 0; i < parArray.length; i++) {
buf.append(parArray[i]);
if ((i + 1) != parArray.length) buf.append(" "); // NOI18N
}
return buf.toString();
}
/**
* Sets whether to use internal execution or not.
*
* @param internal true if the object should be executed by internal execution
* @exception IOException on an error
*/
void setExecution(boolean internal) throws IOException {
FileObject fo = getPrimaryFile();
getPrimaryFile().setAttribute(PROP_EXECUTION, new Boolean (internal));
}
/**
* @return true if the object should be executed internally, false otherwise
*/
boolean getExecution () {
Object o = getPrimaryFile().getAttribute(PROP_EXECUTION);
return o instanceof Boolean ? ((Boolean)o).booleanValue () : false;
}
// other methods ..............................................................................
/** Check if in specific folder exists .ser fileobject with the same name.
* If it exists user is asked for confirmation to rewrite, rename or
* cancel operation. Throws UserCancelException if user pressed cancel
* button.
* @param f destination folder
* @return new Name of file in destination
*/
private String existInFolder(DataFolder f) throws UserCancelException {
FileObject fo = getPrimaryFile();
String name = fo.getName();
String ext = "ser"; // NOI18N
String destName = fo.getName();
if (f.getPrimaryFile().getFileObject(name, ext) != null) {
// file with the same name exists - ask user what to do
ResourceBundle bundle = NbBundle.getBundle(ClassDataObject.class);
String rewriteStr = bundle.getString("CTL_Rewrite");
String renameStr = bundle.getString("CTL_Rename");
String cancelStr = bundle.getString("CTL_Cancel");
NotifyDescriptor nd = new NotifyDescriptor.Confirmation(
new MessageFormat(bundle.getString("MSG_SerExists")).
format(new Object[] { name, f.getName() }));
nd.setOptions(new Object[] { rewriteStr, renameStr, cancelStr });
String retStr = (String)TopManager.getDefault().notify(nd);
if (cancelStr.equals(retStr)) // user cancelled the dialog
throw new UserCancelException();
if (renameStr.equals(retStr))
destName = FileUtil.findFreeFileName (
f.getPrimaryFile(), destName, ext);
if (rewriteStr.equals(retStr)) {
try {
FileObject dest = f.getPrimaryFile().getFileObject(name, ext);
FileLock lock = dest.lock();
dest.delete(lock);
lock.releaseLock();
}
catch (IOException e) {
return null;
}
}
}
return destName;
}
// =============== The mechanism for regeisteing node factories ==============
/**
* @associates ClassElementNodeFactory
*/
private static ArrayList explorerFactories = new ArrayList();
/**
* @associates ClassElementNodeFactory
*/
private static ArrayList browserFactories = new ArrayList();
static {
explorerFactories.add( new ClassElementNodeFactory() );
ClassElementNodeFactory cef = new ClassElementNodeFactory();
cef.setGenerateForTree (true);
browserFactories.add( cef ) ;
}
public static void addExplorerFilterFactory( FilterFactory factory ) {
addFactory( explorerFactories, factory );
}
public static void removeExplorerFilterFactory( FilterFactory factory ) {
removeFactory( explorerFactories, factory );
}
static ElementNodeFactory getExplorerFactory() {
return (ElementNodeFactory)explorerFactories.get( explorerFactories.size() - 1);
}
public static void addBrowserFilterFactory( FilterFactory factory ) {
addFactory( browserFactories, factory );
}
public static void removeBrowserFilterFactory( FilterFactory factory ) {
removeFactory( browserFactories, factory );
}
static ElementNodeFactory getBrowserFactory() {
return (ElementNodeFactory)browserFactories.get( browserFactories.size() - 1 );
}
private static synchronized void addFactory( List factories, FilterFactory factory ) {
factory.attachTo( (ElementNodeFactory)factories.get( factories.size() - 1 ) );
factories.add( factory );
}
private static synchronized void removeFactory( List factories, FilterFactory factory ) {
int index = factories.indexOf( factory );
if ( index <= 0 )
return;
else if ( index == factories.size() - 1 )
factories.remove( index );
else {
((FilterFactory)factories.get( index + 1 )).attachTo( (ElementNodeFactory)factories.get( index - 1 ) );
factories.remove( index );
}
}
// innerclasses .......................................................
/** The implementation of the source cookie.
* Class data object cannot implement source cookie directly,
* because it's optional (if there's no instance cookie, then also
* no source cookie is supported)
*/
private static final class SourceSupport extends Object
implements SourceCookie {
/** The class which acts as a source element data */
private Class data;
/** Reference to outer class */
private ClassDataObject cdo;
/** Creates source support with asociated class object */
SourceSupport (Class data, ClassDataObject cdo) {
this.data = data;
this.cdo = cdo;
}
/** @return The source element for this class data object */
public SourceElement getSource () {
return new SourceElement(new SourceElementImpl(data, cdo));
}
} // the end of SourceSupport inner class
/* PENDING - not reimpl yet
static class BeanTransferableOwner extends TransferableOwner.Filter {
String beanName;
BeanTransferableOwner (
TransferableOwner transferable,
String beanName,
Class beanClass
) {
super (
transferable,
new DataFlavor[] {new TransferFlavors.BeanFlavor (beanClass)}
);
this.beanName = beanName;
} */
/** Creates transferable data for this flavor.
*/
/*
public Object getTransferData (DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (isDataFlavorSupported(flavor)) {
if (flavor instanceof TransferFlavors.BeanFlavor) return beanName;
return super.getTransferData (flavor);
}
else {
// not supported flavor
throw new UnsupportedFlavorException (flavor);
}
}
} */
}
/*
* Log
* 29 Gandalf 1.28 1/20/00 David Simonek #2119 bugfix
* 28 Gandalf 1.27 1/18/00 David Simonek Execution now correctly
* disabled for ser data nodes
* 27 Gandalf 1.26 1/13/00 David Simonek i18n
* 26 Gandalf 1.25 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 25 Gandalf 1.24 8/18/99 Jaroslav Tulach #2641
* 24 Gandalf 1.23 7/25/99 Ian Formanek Fixed bug #2745 -
* Property "Class Name" of serialized prototypes displays the file name
* rather than the name of the class that is serialized in it.
* 23 Gandalf 1.22 7/16/99 Petr Jiricka Fixed bug that classes
* without main couldn't be executed
* 22 Gandalf 1.21 7/9/99 Petr Hrebejk Add/emove mehods made
* synchronized
* 21 Gandalf 1.20 6/28/99 Petr Hrebejk Multiple node factories
* added
* 20 Gandalf 1.19 6/24/99 Jesse Glick Gosh-honest HelpID's.
* 19 Gandalf 1.18 6/22/99 Ian Formanek employed DEFAULT_HELP
* 18 Gandalf 1.17 6/9/99 Ian Formanek ---- Package Change To
* org.openide ----
* 17 Gandalf 1.16 4/8/99 David Simonek obscure dialog bugfix
* (#1411)
* 16 Gandalf 1.15 4/2/99 Jan Jancura ObjectBrowser support
* II.
* 15 Gandalf 1.14 4/1/99 Ian Formanek Rollback to make it
* compilable
* 14 Gandalf 1.13 4/1/99 Jan Jancura Object browser support
* 13 Gandalf 1.12 3/26/99 Ian Formanek
* 12 Gandalf 1.11 3/26/99 Ian Formanek Fixed use of obsoleted
* NbBundle.getBundle (this)
* 11 Gandalf 1.10 3/3/99 Jaroslav Tulach Uses ExecSupport to
* provide DebuggerCookie
* 10 Gandalf 1.9 2/5/99 David Simonek
* 9 Gandalf 1.8 2/3/99 David Simonek
* 8 Gandalf 1.7 2/1/99 David Simonek
* 7 Gandalf 1.6 1/26/99 David Simonek util.Task used for
* synchronization
* 6 Gandalf 1.5 1/22/99 David Simonek synchronization problems
* concerning getCookie repaired
* 5 Gandalf 1.4 1/20/99 David Simonek rework of class DO
* 4 Gandalf 1.3 1/19/99 David Simonek
* 3 Gandalf 1.2 1/13/99 David Simonek
* 2 Gandalf 1.1 1/6/99 Ian Formanek Reflecting change in
* datasystem package
* 1 Gandalf 1.0 1/5/99 Ian Formanek
* $
* Beta Change History:
* 0 Tuborg 0.20 --/--/98 Jan Formanek SWITCHED TO NODES
* 0 Tuborg 0.21 --/--/98 Jan Formanek bugfix
* 0 Tuborg 0.22 --/--/98 Jan Formanek added property showDeclaredOnly
* 0 Tuborg 0.23 --/--/98 Jan Formanek employed PropertySupport.ReadOnly, ...
* 0 Tuborg 0.24 --/--/98 Petr Hamernik initializing of subnodes improvements
* 0 Tuborg 0.25 --/--/98 Jan Formanek icon change
* 0 Tuborg 0.26 --/--/98 Jan Formanek checks if the class has the right main method in
* 0 Tuborg 0.26 --/--/98 Jan Formanek isExecutionAllowed
* 0 Tuborg 0.27 --/--/98 Jan Formanek different icon for classes with main() method
* 0 Tuborg 0.28 --/--/98 Jan Formanek NotSerializable bug fixed (BUG ID: 03210062)
* 0 Tuborg 0.29 --/--/98 Jan Formanek default is showDeclaredOnly
* 0 Tuborg 0.31 --/--/98 Jan Jancura default is showDeclaredOnly
* 0 Tuborg 0.32 --/--/98 Jan Jancura moved to propertySet
* 0 Tuborg 0.34 --/--/98 Ales Novak handler joined
* 0 Tuborg 0.36 --/--/98 Jan Formanek isExecAllowed from ExecCookie added, isExecutionAllowed removed
* 0 Tuborg 0.36 --/--/98 Jan Formanek CustomizeBeanCookie implementation
* 0 Tuborg 0.37 --/--/98 Jan Jancura exception in containsMain...
* 0 Tuborg 0.38 --/--/98 Jaroslav Tulach defaultClipboardCut moved to ClassDataNode
* 0 Tuborg 0.41 --/--/98 Jan Jancura bugfix
* 0 Tuborg 0.44 --/--/98 Ales Novak applets support
* 0 Tuborg 0.45 --/--/98 Jan Formanek bugfix in isApplet () (thrown ClassFormatError if the class was in
* 0 Tuborg 0.45 --/--/98 Jan Formanek a wrong package)
*/